﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

namespace FunctionalCLR.Core {
    public class DefaultFunctionLoader {

        internal IFunctionManager funMgr = null;

        public DefaultFunctionLoader(IFunctionManager fm) {
            this.funMgr = fm;
        }

        public void Load(string location) {
            string[] fileNames = Directory.GetFiles(location);
            foreach (string fileName in fileNames) {
                if (fileName.EndsWith(".exe") || fileName.EndsWith(".dll")) {
                    try {
                        Utils.Debugging("File: {0}", fileName);
                        LoadFromAssembly(fileName);
                    } catch (Exception ex) {
                        Utils.Debugging(ex.Message);
                    }
                }
            }
        }

        public IProgressReporter ProgressReporter {
            get;
            set;
        }

        public void LoadFromAssembly(string fileName) {
            try {
                Assembly assembly = Assembly.LoadFrom(fileName);
                Type[] types = assembly.GetTypes();

                foreach (Type type in types) {
                    FuncNode instance = null;

                    Utils.Debugging("Loading function type: " + type.Name);
                    if (type.IsSubclassOf(typeof(FuncNode))) {
                        try {
                            Utils.Debugging("Loading function: " + type.Name);
                            if (ProgressReporter != null) {
                                ProgressReporter.Write(type.Name);
                            }

                            instance = LoadFunction(assembly, type.FullName);
                            if (!string.IsNullOrEmpty(instance.Value)) {
                                if (this.funMgr.Exist(instance.Value)) {
                                    Console.WriteLine(
                                        "Function '{0}' was added before.", 
                                        instance.Value);
                                    continue;
                                }

                                this.funMgr.Add(instance.Value, type);
                                Utils.Debugging(
                                    "Function '{0}' was loaded successfully.", 
                                    instance.Value);
                            }
                        } catch (Exception ex) {
                            Utils.Debugging(ex.Message + "\n" + ex.StackTrace);
                        }
                    }
                }
            } catch (Exception ex) {
                Utils.Debugging(ex.Message + "\n" + ex.StackTrace);
                throw ex;
            }
        }

        public virtual FuncNode LoadFunction(Assembly assembly, string p) {
            return assembly.CreateInstance(p) as FuncNode;
        }
    }
}
